/* Microchip ENC28J60 driver
 *
 * Includes errata up to B7 (Hardware version 6)
 */

/* Copyright (C) 2009 David Zanetti
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include <avr/io.h>
#include <avr/iom644.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>

#include "serial.h"
#include "spi.h"
#include "i2c.h"
#include "enc28j60.h"
#include "global.h"
#include <util/delay.h>

/* register names */
/* bank 0 */
#define ERDPTL 0,0x00
#define ERDPTH 0,0x01
#define EWRPTL 0,0x02
#define EWRPTH 0,0x03
#define ETXSTL 0,0x04
#define ETXSTH 0,0x05
#define ETXNDL 0,0x06
#define ETXNDH 0,0x07
#define ERXSTL 0,0x08
#define ERXSTH 0,0x09
#define ERXNDL 0,0x0a
#define ERXNDH 0,0x0b
#define ERXRDPTL 0,0x0c
#define ERXRDPTH 0,0x0d
#define ERXWRPTL 0,0x0e
#define ERXWRPTH 0,0x0f
#define EDMASTL 0,0x10
#define EDMASTH 0,0x11
#define EDMANDL 0,0x12
#define EDMANDH 0,0x13
#define EDMADSTL 0,0x14
#define EDMADSTH 0,0x15
#define EDMACSL 0,0x16
#define EDMACSH 0,0x17

#define EIE 0,0x1b /* global */
#define INTIE 7
#define PKTIE 6
#define DMAIE 5
#define LINKIE 4
#define TXIE 3
#define TXERIE 1
#define RXERIE 0

#define EIR 0,0x1c /* global */
#define PKTIF 6
#define DMAIF 5
#define LINKIF 4
#define TXIF 3
#define TXERIF 1
#define RXERIF 0

#define ESTAT 0,0x1d /* global */
#define INT 7
#define BUFER 6
#define LATECOL 4
#define RXBUSY 2
#define TXABRT 1
#define CLKRDY 0

#define ECON2 0,0x1e /* global */
#define AUTOINC 7
#define PKTDEC 6
#define PWRSV 5
#define VRPS 3

#define ECON1 0,0x1f /* global */
#define TXRST 7
#define RXRST 6
#define DMAST 5
#define CSUMEN 4
#define TXRTS 3
#define RXEN 2
#define BSEL1 1
#define BSEL0 0

/* bank 1 */
#define EHT0 1,0x00 
#define EHT1 1,0x01
#define EHT2 1,0x02
#define EHT3 1,0x03
#define EHT4 1,0x04
#define EHT5 1,0x05
#define EHT6 1,0x06
#define EHT7 1,0x07
#define EPMM0 1,0x08
#define EPMM1 1,0x09
#define EPPM2 1,0x0a
#define EPPM3 1,0x0b
#define EPMM4 1,0x0c
#define EPMM5 1,0x0d
#define EPMM6 1,0x0e
#define EPMM7 1,0x0f
#define EPMCSL 1,0x10
#define EPMCSH 1,0x11
#define EPMOL 1,0x14
#define EPMOH 1,0x15

#define ERXFCON 1,0x18
#define UCEN 7
#define ANDOR 6
#define CRCEN 5
#define PMEN 4
#define MPEN 3
#define HTEN 2
#define MCEN 1
#define BCEN 0

#define EPKTCNT 1,0x19
/* bank 2 */
#define MACON1 2,0x00
#define TXPAUS 3
#define RXPAUS 2
#define PASSALL 1
#define MARXEN 0

#define MACON3 2,0x02
#define PADCFG2 7
#define PADCFG1 6
#define PADCFG0 5
#define TXCRCEN 4
#define PHDREN 3
#define HFRMEN 2
#define FRMLNEN 1
#define FULDPX 0

#define MACON4 2,0x03
#define DEFER 6
#define BPEN 5
#define NOBKOFF 4

#define MABBIPG 2,0x04
#define MAIPGL 2,0x06
#define MAIPGH 2,0x07
#define MACLCON1 2,0x08
#define MACLCON2 2,0x09
#define MAMXFLL 2,0x0a
#define MAMXFLH 2,0x0b

#define MICMD 2,0x12
#define MIISCAN 1
#define MIIRD 0

#define MIREGADR 2,0x14
#define MIWRL 2,0x16
#define MIWRH 2,0x17
#define MIRDL 2,0x18
#define MIRDH 2,0x19
/* bank 3 */
#define MAADR5 3,0x00
#define MAADR6 3,0x01
#define MAADR3 3,0x02
#define MAADR4 3,0x03
#define MAADR1 3,0x04
#define MAADR2 3,0x05
#define EBSTSD 3,0x06

#define EBSTCON 3,0x07
#define PSV2 7
#define PSV1 6
#define PSV0 5
#define PSEL 4
#define TMSEL1 3
#define TMSEL0 2
#define TME 1
#define BISTST 0

#define EBSTCSL 3,0x08
#define EBSTCSH 3,0x09

#define MISTAT 3,0x0a
#define NVALID 2
#define SCAN 1
#define BUSY 0

#define EREVID 3,0x12

#define ECOCON 3,0x15
#define COCON2 2
#define COCON1 1
#define COCON0 0

#define EFLOCON 3,0x17
#define FULDPXS 2
#define FCEN1 1
#define FCEN0 0

#define EPAUSL 3,0x18
#define EPAUSH 3,0x19

/* PHY Registers */
#define PHCON1 0x00
#define PRST 15
#define PLOOPBK 14
#define PPWRSV 11
#define PDPXMD 8

#define PHSTAT1 0x01
#define PFDPX 12
#define PHDPX 11
#define LLSTAT 2
#define JBSTAT 1

#define PHID1 0x02
#define PHID2 0x03

#define PHCON2 0x10
#define FRCLNK 14
#define TXDIS 13
#define JABBER 10
#define HDLDIS 8

#define PHSTAT2 0x11
#define TXSTAT 13
#define RXSTAT 12
#define COLSTAT 11
#define LSTAT 10
#define DPXSTAT 9
#define PLRITY 5

#define PHIE 0x12
#define PLNKIE 4
#define PGEIE 1

#define PHIR 0x13
#define PLNKIF 4
#define PGIF 2

#define PHLCON 0x14
#define LACFG3 11
#define LACFG2 10
#define LACFG1 9
#define LACFG0 8
#define LBCFG3 7
#define LBCFG2 6
#define LBCFG1 5
#define LBCFG0 4
#define LFRQ1 3
#define LFRQ0 2
#define STRCH 1

/* what buffer space we want (tunable) */
#define RX_SIZE 6143

/* largest frame we can transmit or receive */
#define FRAME_MAX 1520

/* some basic defines for various bugs, hardware rev they apply to */
#define BUG_RESET (enc28j60_ver >= 0)
#define BUG_CLKOUT (enc28j60_ver >= 0)
#define BUG_PKTIF (enc28j60_ver >= 0)
#define BUG_HALF_LOOP (enc28j60_ver >= 0)
#define BUG_FULL_LOOP (enc28j60_ver >= 0)
#define BUG_TX_LOCKUP (enc28j60_ver >= 0)
#define BUG_RX_BUF_WRAP (enc28j60_ver >= 0)
#define BUG_HALF_FALSE_COLL (enc28j60_ver >= 0)
#define BUG_DMA_CRC (enc28j60_ver >= 0)

/* What version of hardware we have */
uint8_t enc28j60_ver;

/* default our MAC address */
uint8_t EEMEM enc28j60_mac_eeprom[6] = { 0xae, 0xde, 0x48, 0x00, 0x00, 0x01 };

/* internal prototypes */
/* write to a register */
void enc28j60_writereg(uint8_t bank, uint8_t reg, uint8_t value);

/* read a register */
uint8_t enc28j60_readreg(uint8_t bank, uint8_t reg);

/* read a register from the PHY side of the chip */
uint16_t enc28j60_readphy(uint8_t reg);

/* write to a PHY register */
void enc28j60_writephy(uint8_t reg, uint16_t value);

/* switch banks, used as part of the write/read code */
void enc28j60_switchbank(uint8_t bank);

void enc28j60_readbuffer(uint16_t len, uint8_t *pkt);

void enc28j60_dumpframe(uint8_t *pkt, uint16_t len);

/* BEGIN REAL CODE */

void enc28j60_switchbank(uint8_t bank) {
 static uint8_t cur = -1;
 uint8_t reg;

 /* don't change banks if we are already in that bank */
 if (cur == bank) {
  return;
 }

 /* work out current register value */
 SPI_BEGIN;
 spi_txrx(0x1f); /* ECON1 */
 reg = spi_txrx(0); /* ignore tx */
 SPI_END;

 /* change to the new bank */
 SPI_BEGIN;
 spi_txrx(0x40 | 0x1f); /* write to reg ECON1 */
 spi_txrx((reg & 0xfc) | bank);
 SPI_END;

 /* update our bank cache */ 
 cur = bank;
}

void enc28j60_writereg(uint8_t bank, uint8_t reg, uint8_t value) {

 if (reg < 0x1a) { /* global regs above this */
  enc28j60_switchbank(bank); /* get into the correct bank */
 }
 
 /* now write the register */
 SPI_BEGIN;
 spi_txrx(0x40 | reg);
 spi_txrx(value);
 SPI_END;
}

void enc28j60_setbit(uint8_t bank, uint8_t reg, uint8_t value) {
 if (reg < 0x1a) {
  enc28j60_switchbank(bank);
 }

 /* issue the bit set command */
 SPI_BEGIN;
 spi_txrx(0x80 | reg);
 spi_txrx(1 << value);
 SPI_END;
}

void enc28j60_clearbit(uint8_t bank, uint8_t reg, uint8_t value) {
 if (reg < 0x1a) {
  enc28j60_switchbank(bank);
 }
 
 /* issue bit clear command */
 SPI_BEGIN;
 spi_txrx(0xA0 | reg);
 spi_txrx(1 << value);
 SPI_END;
}

uint8_t enc28j60_readreg(uint8_t bank, uint8_t reg) {
 uint8_t value;

 if (reg < 0x1a) {
  enc28j60_switchbank(bank); /* get into the correct bank */
 }

 SPI_BEGIN;
 spi_txrx(reg);
 if ((bank == 2 && reg < 0x1a) || (bank == 3 && reg < 0x06) ||
      (bank == 3 && reg == 0x0a)) {
  spi_txrx(0); /* random padded byte */
 }
 value = spi_txrx(0); /* transmit is ignored */
 SPI_END;

 return value;
}

uint16_t enc28j60_readphy(uint8_t reg) {
  uint16_t value;
 /* Accessing PHYs is a PITA. Can't be done directly, has to be done 
    indirectly via the main registers */
 /* setup the write */
 enc28j60_writereg(MIREGADR,reg);
 /* poke the controller into getting it */
 enc28j60_writereg(MICMD,(1 << MIIRD)); /* start reading the reg */
 _delay_us(11); 
 if (enc28j60_readreg(MISTAT) & (1 << BUSY)) {
  _delay_us(10);
 }
 /* clear the read command bit */
 enc28j60_writereg(MICMD,0x00);

 /* get the data */
 value = enc28j60_readreg(MIRDL) | (enc28j60_readreg(MIRDH) << 8);

 return value;
}

void enc28j60_writephy(uint8_t reg, uint16_t value) {
 /* writes have to go thru the main registers */
 enc28j60_writereg(MIREGADR,reg);
 enc28j60_writereg(MIWRL,(value & 0x00ff));
 enc28j60_writereg(MIWRH,(value >> 8));
 /* write is automatically started, poll for completion */
 _delay_us(11);
 if (enc28j60_readreg(MISTAT) & (1 << BUSY)) {
  _delay_us(10);
 }
 return;
}

void enc28j60_softreset(void) {
 /* perform an SPI-initiated soft reset of the chip */
 /* note: must call init after this */
 serial0_tx_PGM(PSTR("enc28j60: soft reset\r\n"));
 SPI_BEGIN;
 spi_txrx(0xff);
 SPI_END;
 /* Errata B7: can't just poll CLKRDY */
 _delay_ms(1);
}

void enc28j60_hardreset(void) {
 /* this actually pulls the reset line of the chip low, then
  * high */
#ifdef DEBUG_ENC28J60
 serial0_tx_PGM(PSTR("enc28j60: hard reset\r\n"));
#endif
 DDRB |= (1 << PORTB3); /* portb3 is output */
 PORTB &= (1 << PORTB3); /* bring low */
 _delay_ms(10); /* must be held low for long enough to force reset */

 PORTB |= (1 << PORTB3); /* up */
 /* just like soft reset, this is probably buggy */
 _delay_ms(10);
} 

void enc28j60_readbuffer(uint16_t len, uint8_t *pkt) {
 SPI_BEGIN;
 spi_txrx(0x3a); /* read buffer command */
 while (len--) {
  *pkt++ = spi_txrx(0x00);
 }
 SPI_END;
} 

void enc28j60_init(void) {
 uint8_t n;

 spi_init();

 for (n = 0; n < 6; n++) {
  enc28j60_hardreset();

  /* find out MAC and plug it in */

  /* query i2c eeprom for mac address */

  /* i2c eeprom is at i2c address 0x50, mac address starts in 0xfa in eeprom */  
  if (i2c_read(0x50,0xfa,(uint8_t *) eth_mac,6) != 6) {
   serial0_tx_PGM(PSTR("enc28j60: falling back to local EEPROM\r\n"));
   eeprom_read_block((void *) &eth_mac, (const void *) &enc28j60_mac_eeprom, 6);
  };

  if (eth_mac[0] == 0xff) {
   serial0_tx_PGM(PSTR("enc28j60: MAC invalid. Refusing to init.\r\n"));
   return;
  }

  /* write the MAC to the registers */
  enc28j60_writereg(MAADR1,eth_mac[0]);
  if (enc28j60_readreg(MAADR1) != eth_mac[0]) {
   _delay_ms(1000);
   continue;
  }
  enc28j60_writereg(MAADR2,eth_mac[1]);
  if (enc28j60_readreg(MAADR2) != eth_mac[1]) {
   _delay_ms(1000);
   continue;
  }
  enc28j60_writereg(MAADR3,eth_mac[2]);
  if (enc28j60_readreg(MAADR3) != eth_mac[2]) {
   _delay_ms(1000);
   continue;
  }
  enc28j60_writereg(MAADR4,eth_mac[3]);
  if (enc28j60_readreg(MAADR4) != eth_mac[3]) {
   _delay_ms(1000);
   continue;
  }
  enc28j60_writereg(MAADR5,eth_mac[4]);
  if (enc28j60_readreg(MAADR5) != eth_mac[4]) {
   _delay_ms(1000);
   continue;
  }
  enc28j60_writereg(MAADR6,eth_mac[5]);
  if (enc28j60_readreg(MAADR6) != eth_mac[5]) {
   _delay_ms(1000);
   continue;
  }

  /* report version of the chip */
  /* version is also used to detect bugs */
  enc28j60_ver = enc28j60_readreg(EREVID);
  #ifdef DEBUG_ENC28J60
  serial0_tx_PGM(PSTR("enc28j60: Microchip ENC28J60 version "));
  serial0_tx_dec(enc28j60_ver);
  serial0_tx_cr();
  #endif

  /* report mac */
  serial0_tx_PGM(PSTR("enc28j60: MAC Address "));
  serial0_tx_hex(eth_mac[0]);
  serial0_tx_cout(':');
  serial0_tx_hex(eth_mac[1]);
  serial0_tx_cout(':');
  serial0_tx_hex(eth_mac[2]);
  serial0_tx_cout(':');
  serial0_tx_hex(eth_mac[3]);
  serial0_tx_cout(':');
  serial0_tx_hex(eth_mac[4]);
  serial0_tx_cout(':');
  serial0_tx_hex(eth_mac[5]);
  serial0_tx_cr();
  
  break;
 }

 if (n == 6) {
  serial0_tx_PGM(PSTR("enc28j60: too many retries, giving up\r\n"));
  return;
 }

 /* set up RX and TX buffer space */
 /* read low point */
 enc28j60_writereg(ERXSTH,0);
 enc28j60_writereg(ERXSTL,0); 
 /* read high point */
 enc28j60_writereg(ERXNDL,RX_SIZE & 0x00ff); 
 enc28j60_writereg(ERXNDH,RX_SIZE >> 8);

 /* Errata B7 */
 enc28j60_writereg(ERDPTL,0);
 enc28j60_writereg(ERDPTH,0);
 enc28j60_writereg(ERXRDPTL,0);
 enc28j60_writereg(ERXRDPTH,0);

 /* enable reveive of frames, honor received pauses */
 enc28j60_writereg(MACON1,(1 << MARXEN) | (1 << RXPAUS) | (1 << TXPAUS)); 

 /* configure packet padding */
 /* pad to 60 (64 for 802.1q, generate CRC, check length */
 enc28j60_writereg(MACON3,(1 << PADCFG2) | (1 << PADCFG0) | (1 << TXCRCEN) |
                     (1 << FRMLNEN));

 /* backpressure settings */
 enc28j60_writereg(MACON4,(1 << DEFER)); /* defer indefinitely on busy */

 /* set maximum frame size */
 enc28j60_writereg(MAMXFLL,FRAME_MAX & 0x00ff);
 enc28j60_writereg(MAMXFLH,FRAME_MAX >> 8);

 /* back to back inter-packet gap */
 enc28j60_writereg(MABBIPG,0x12); /* datasheet suggestion */

 /* non-back to back inter-packet gap */
 enc28j60_writereg(MAIPGL,0x12); /* datasheet default */

 /* other half of non-back to back inter-packet gap */
 enc28j60_writereg(MAIPGH,0x0c); /* datasheet default */

 /* filter packets as follows */
 /* enable unicast, enable pattern match */
 enc28j60_writereg(ERXFCON,(1 << UCEN) | (1 << BCEN)); 
 enc28j60_writereg(EPMM0,0x3f);
 enc28j60_writereg(EPMM1,0x30);
 enc28j60_writereg(EPMCSL,0xf9);
 enc28j60_writereg(EPMCSH,0xf7);
 /* the above pattern match came from the Arduino libraries
  * limits broadcast to ARP only. */

 /* PHY INIT */
 
 /* enable full duplex */
/*
 enc28j60_writephy(PHCON1,(1 << PDPXMD));  */
 enc28j60_writephy(PHCON1,0); /* make sure it's forced to half */

 /* disable loopback */
 /* Must be forced on, Errata B7 */
 enc28j60_writephy(PHCON2,(1 << HDLDIS));

 /* enable interrupts for link state on PHY */
 enc28j60_writephy(PHIE,(1 << PLNKIE) | (1 << PGEIE)); 

 /* get things moving - tx run, rx run, dma checksum, rx enable */
 enc28j60_setbit(ECON1,CSUMEN);
 enc28j60_setbit(ECON1,RXEN);

 /* finally, generate interupts for various events */
 enc28j60_setbit(EIE,PKTIE); /* RX waiting */
 enc28j60_setbit(EIE,LINKIE); /* Link State Changes */
 enc28j60_setbit(EIE,TXIE); /* TX complete */
 enc28j60_setbit(EIE,RXERIE); /* RX failed, usually too many packets */
 enc28j60_setbit(EIE,INTIE); /* Global Interrupts enable */
}

uint8_t enc28j60_tx(void *pkt, uint16_t len) {
 uint16_t n;
 /* step thru the packet, writing out the contents */

 /* TX logic may lock up occasionally. Reset it before attempting
  * to transmit packets. See Errata B7 */
  enc28j60_setbit(ECON1,TXRST);
 _delay_us(10);
 enc28j60_clearbit(ECON1,TXRST);
 /* and this might get set too! */
 enc28j60_clearbit(EIR,TXERIF);

 /* dump the packet */
/* enc28j60_dumpframe((uint8_t *)pkt, len); */

 /* set the start of the tx buffer to receive plus 1, since it will be free */
 enc28j60_writereg(ETXSTL,((RX_SIZE+1) & 0x00ff)); 
 enc28j60_writereg(ETXSTH,((RX_SIZE+1) >> 8));
 /* set the write pointer to the same place */
 enc28j60_writereg(EWRPTL,((RX_SIZE+1) & 0x00ff));
 enc28j60_writereg(EWRPTH,((RX_SIZE+1) >> 8));

 /* start the write */
 SPI_BEGIN;
 spi_txrx(0x7a); /* Write Buffer Commence */
 spi_txrx(0x00); /* Packet options - override none */
 for (n = 0; n < len; n++) {
  /* write out the pkacet contents */
  spi_txrx(((uint8_t *)pkt)[n]); /* YAY VOID POINTERS */
 }
 SPI_END;
 /* write completed */
 /* point the write end point to our packet end address */
 n = RX_SIZE+1+len;
 enc28j60_writereg(ETXNDL,(n & 0x00ff));
 enc28j60_writereg(ETXNDH,(n >> 8));
 /* now transmit it */
 enc28j60_setbit(ECON1,TXRTS); 
 
 /* loop to check if we completed this send */
 n = 1;
 while (n < 10000 && enc28j60_readreg(ECON1) & (1 << TXRTS) &&
        !(enc28j60_readreg(EIR) & (1 << TXERIF))) {
  n++;
  _delay_us(11);
 }  

 if (enc28j60_readreg(ESTAT) & (1 << TXABRT)) {
  serial0_tx_PGM(PSTR("enc28j60: tx was aborted\r\n"));
  return 0;
 }

 if (n == 10000 || enc28j60_readreg(ECON1) & (1 << TXERIF)) {
  serial0_tx_PGM(PSTR("enc28j60: tx failure\r\n"));
  return 0;
 }

 global_counters.eth_pkt_tx++;

 return 1; 
} 

uint16_t enc28j60_rx(uint8_t *dest, uint16_t maxlen) {
 static uint16_t next = 0;
 uint16_t status;
 uint16_t len = 0;

 /* first, check to see if we have any packets to get */
 if (!(enc28j60_readreg(EIR) & (1 << PKTIF))) {
  /* hmm, seems to be none */
  /* Oh wait, Errata B7 says it tells lies! */
  if (enc28j60_readreg(EPKTCNT) == 0) {
   return 0;
  }
 }

 /* in theory we really have a packet now to process */
 
 /* point the read pointer at where the last packet's next
  * pointed to */ 
 enc28j60_writereg(ERDPTL, next & 0xff);
 enc28j60_writereg(ERDPTH, next >> 8);   

 enc28j60_readbuffer(2,(uint8_t *)&next);
 enc28j60_readbuffer(2,(uint8_t *)&len);  
 enc28j60_readbuffer(2,(uint8_t *)&status);


 /* data is after this point */
 if (len > maxlen) {
  serial0_tx_PGM(PSTR("enc28j60: packet is longer than RX buf\r\n"));
  len = maxlen;
 }

 /* copy the packet into the destination */
 enc28j60_readbuffer(len,dest);


 /* move the pointer .. errata B7 requires it this way */
 {
   uint16_t rxst, rxnd;
   rxst = enc28j60_readreg(ERXSTH) << 8;
   rxst |= enc28j60_readreg(ERXSTL);
   rxnd = enc28j60_readreg(ERXNDH) << 8;
   rxnd |= enc28j60_readreg(ERXNDL);
   
   if (next-1 < rxst || next-1 > rxnd) {
    enc28j60_writereg(ERXRDPTL,rxnd & 0xff);
    enc28j60_writereg(ERXRDPTH,rxnd >> 8);
   } else { 
    /* next--; */
    enc28j60_writereg(ERXRDPTL,next & 0xff);
    enc28j60_writereg(ERXRDPTH,next >> 8);
   }
 }

 /* and indicate we have processed this packet */
 enc28j60_setbit(ECON2,PKTDEC);
 _delay_us(11); /* seems to be about the cycle for the chip */

 /* FIXME: this calculation is overwriting random data */
 /* zero out the rest of dest */
// for (maxlen -= len; maxlen; maxlen--) {
//  *(dest+len+maxlen) = 0;
// }

 global_counters.eth_pkt_rx++;

 return len;
}

void enc28j60_dumpframe(uint8_t *pkt, uint16_t len) {
 uint16_t n = 0;
 for (n = 0; n < len; n++) {
  if ((n % 16) == 0) {
   serial0_tx_cr();
   serial0_tx_hex(n >> 8);
   serial0_tx_hex(n & 0xff);
   serial0_tx_cout(':');
   serial0_tx_cout(' ');
  }
  serial0_tx_hex(pkt[n]);
  serial0_tx_cout(' ');
 }
 serial0_tx_cr();
}

/* process an interrupt we got */
void enc28j60_interrupt(void) {
 uint8_t status;

#ifdef DEBUG_ENC28J60
 serial0_tx_PGM(PSTR("enc28j60: interrupt\r\n"));
#endif

 /* find out what the cause is */
 status = enc28j60_readreg(EIR);

 if (!(BUG_PKTIF)) { 
  if (status & (1 << PKTIF)) {
 #ifdef DEBUG_ENC28J60
   serial0_tx_PGM(PSTR("enc28j60: one or more unprocessed packets\r\n"));
 #endif
   set_flag(FLAG_EINT_RX);
 //  enc28j60_clearbit(EIR,PKTIF);
  }
 } else {
  /* PKTIF is unreliable, check packet count instead */
  if (enc28j60_readreg(EPKTCNT) > 0) {
   set_flag(FLAG_EINT_RX);
  }
 }

 if (status & (1 << DMAIF)) {
#ifdef DEBUG_ENC28J60
  serial0_tx_PGM(PSTR("enc28j60: DMA complete\r\n"));
#endif
  enc28j60_clearbit(EIR,DMAIF);
 }

 if (status & (1 << LINKIF)) {
  uint16_t phy;

#ifdef DEBUG_ENC28J60
  serial0_tx_PGM(PSTR("enc28j60: Link status changed\r\n"));
#endif
  enc28j60_clearbit(EIR,LINKIF);
  /* retreieve linkstate from PHY */
  phy = enc28j60_readphy(PHIR);
  if (phy & (1 << PLNKIF)) {
   /* finf out what the state actually is now */
   phy = enc28j60_readphy(PHSTAT2);
   if (phy & (1 << LSTAT)) {
    serial0_tx_PGM(PSTR("enc28j60: link up\r\n"));
    set_flag(FLAG_ENET_LINK);
   } else {
    serial0_tx_PGM(PSTR("enc28j60: link down\r\n"));
    clear_flag(FLAG_ENET_LINK);
   }
  }
 }

 if (status & (1 << TXIF)) {
#ifdef DEBUG_ENC28J60
  serial0_tx_PGM(PSTR("enc28j60: TX complete\r\n"));
#endif
  enc28j60_clearbit(EIR,TXIF);
 }

 if (status & (1 << TXERIF)) {
#ifdef DEBUG_ENC28J60
  serial0_tx_PGM(PSTR("enc28j60: TX Error\r\n"));
#endif
  enc28j60_clearbit(EIR,TXERIF);
 }

 if (status & (1 << RXERIF)) {
#ifdef DEBUG_ENC28J60
  serial0_tx_PGM(PSTR("enc28j60: RX Error\r\n"));
#endif
  enc28j60_clearbit(EIR,RXERIF);
 }

 return;
}
